//------------------------------------------------------------
//  Filename: eth_mac_txdma.sv
//   
//  Author  : wlduan@gmail.com
//  Revise  : 2020-12-01 19:05
//  Description: 
//   
//  All Rights Reserved.                                       
//-------------------------------------------------------------
//
`timescale 1ns/1ps
 
module eth_mac_txdma #(
    parameter BD_WCNT = 2
)( 
    input  logic       clk_i,
    input  logic       rstn_i,
 
    input  logic[3:0]  mac_tx_ifg,

    output logic[9:0]  dma_tx_data,
    output logic       dma_tx_valid,
    input  logic       dma_tx_ready,

    input  logic       dma_tx_need,
    input  logic       dma_tx_en,
    output logic       dma_tx_busy,
    output logic[3:0]  dma_tx_err,
    output logic[6:0]  dma_tx_rptr,

    BDU_IF.Master      bdu_master,
    BDU_IF.Master      mem_master 
);
//--------------------------------------------------------
logic[15:0] tx_bcnt;
logic[31:0] tx_wbuf;
logic       tx_sof ;
logic       tx_eof ;
logic[15:0] bdu_sz;
logic       bdu_sof;
logic       bdu_eof;
logic       bdu_req;
logic       bdu_next;
logic[3:0]  bdu_rd_cnt;
logic       bdu_rd_cnt_q;
logic       dma_reset;
logic       dma_bdu_op;    
logic[31:0] dma_bdu_addr;
logic[31:0] dma_mem_addr;
logic[8:0]  ifg_set;
logic       ifg_end;
//--------------------------------------------------------
assign dma_tx_data = {tx_sof,tx_eof,tx_wbuf[7:0]};
//--------------------------------------------------------
enum logic[3:0] {DMA_IDLE,DMA_NOP,DMA_CSR,DMA_DATA,DMA_LAST,DMA_WIT,DMA_GAP} dma_cs,dma_ns;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_cs <= DMA_IDLE;
    end
    else begin 
        dma_cs <= dma_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    dma_ns = dma_cs;
    case(dma_cs) 
        DMA_IDLE: begin 
            if(dma_reset) dma_ns = DMA_NOP ;
        end
        DMA_NOP: begin 
            dma_ns = DMA_CSR;
        end
        DMA_CSR: begin 
            if(bdu_rd_cnt == BD_WCNT ) dma_ns = DMA_DATA;
        end
        DMA_DATA : begin 
            if(tx_bcnt == bdu_sz) dma_ns = DMA_LAST;
        end
        DMA_LAST : begin 
            dma_ns = DMA_WIT;
        end
        DMA_WIT : begin 
            dma_ns = DMA_GAP;
        end
        DMA_GAP : begin 
            if(ifg_end) dma_ns = DMA_IDLE;
        end
    endcase 
end
//--------------------------------------------------------
//----
//----
//--------------------------------------------------------
enum logic[2:0] {MST_IDLE,WAIT_GNT,WAIT_VLD} bdu_cs,bdu_ns;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_cs <= MST_IDLE;
    end
    else begin 
        bdu_cs <= bdu_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    bdu_ns = bdu_cs;
    bdu_master.addr  = dma_bdu_addr;
    bdu_master.req   = 'b0;
    bdu_master.we    = 'b0;
    bdu_master.wdata = 'b0;
 
    case(bdu_cs) 
        MST_IDLE: begin
            if(dma_bdu_op) begin 
                bdu_master.req = bdu_req;
                if(bdu_req) bdu_ns = (bdu_master.gnt)? WAIT_VLD : WAIT_GNT;
            end
        end
        WAIT_GNT: begin
            bdu_master.req = 1;
            if(bdu_master.gnt) bdu_ns = WAIT_VLD;
        end
        WAIT_VLD: begin 
            if(bdu_master.rvalid) begin 
                bdu_master.req = bdu_req;
                bdu_ns = (bdu_req&dma_bdu_op)?((bdu_master.gnt)? WAIT_VLD : WAIT_GNT) : MST_IDLE;
            end
        end
    endcase 
end
//--------------------------------------------------------
assign dma_bdu_op = (dma_cs == DMA_CSR);
assign bdu_req    = (dma_cs == DMA_CSR)&(bdu_rd_cnt < BD_WCNT);
assign bdu_rd_cnt = (bdu_master.rvalid)?(bdu_rd_cnt_q + 1'b1):bdu_rd_cnt_q;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_bdu_addr <= 0;
    end
    else if (dma_reset) begin 
        dma_bdu_addr <= {1'b1,dma_tx_rptr,3'b000};
    end
    else if (bdu_master.req&bdu_master.gnt) begin 
        dma_bdu_addr <= {1'b1,dma_tx_rptr,3'b100};
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_tx_rptr <= '0;
    end
    else if (dma_cs == DMA_LAST) begin 
        dma_tx_rptr <= dma_tx_rptr + 1'b1;
    end
end 
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_rd_cnt_q <= '0;
    end
    else if (dma_cs == DMA_IDLE) begin 
        bdu_rd_cnt_q <= 'b0;
    end
    else if(bdu_master.rvalid) begin 
        bdu_rd_cnt_q <= bdu_rd_cnt;
    end
end 
//--------------------------------------------------------
//----
//----
//--------------------------------------------------------
enum logic[4:0] {MEM_IDLE,MEM_GNT,MEM_VLD,MEM_TXD0,MEM_TXD1,MEM_TXD2,MEM_TXD3} mem_cs,mem_ns;
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        mem_cs <= MEM_IDLE ;
    end
    else begin 
        mem_cs <= mem_ns;
    end
end
//--------------------------------------------------------
always_comb begin 
    mem_ns = mem_cs;
    mem_master.addr = dma_mem_addr;
    mem_master.wdata= 'b0;
    mem_master.req  = 'b0;
    mem_master.we   = 'b0;
 
    case(mem_cs) 
        MEM_IDLE: begin 
            if(dma_cs == DMA_DATA) begin 
                mem_master.req = 1'b1;
                mem_ns = (mem_master.gnt)? MEM_VLD : MEM_GNT;
            end
        end
        MEM_GNT: begin 
            mem_master.req = 1'b1;
            if(mem_master.gnt) mem_ns = MEM_VLD;
        end
        MEM_VLD: begin 
            if(mem_master.rvalid) mem_ns = MEM_TXD0;
        end
        MEM_TXD0: begin
            if(dma_tx_ready) mem_ns = MEM_TXD1;
        end
        MEM_TXD1: begin
            if(dma_tx_ready) mem_ns = MEM_TXD2;
        end
        MEM_TXD2: begin
            if(dma_tx_ready) mem_ns = MEM_TXD3;
        end
        MEM_TXD3: begin
            if(dma_tx_ready) mem_ns = MEM_IDLE;
        end
    endcase 

    if(bdu_next) mem_ns = MEM_IDLE;
 
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_mem_addr <= 0;
    end
    else if ((bdu_rd_cnt_q == 0)&bdu_master.rvalid) begin 
        dma_mem_addr <= bdu_master.rdata;
    end
    else if(mem_master.gnt)begin 
        dma_mem_addr <= {{dma_mem_addr[31:2] + 1'b1},2'b00};
    end
end
//--------------------------------------------------------
assign dma_tx_valid = ((tx_bcnt < bdu_sz)&&((mem_cs == MEM_TXD0)|(mem_cs == MEM_TXD1)|(mem_cs == MEM_TXD2)|(mem_cs == MEM_TXD3))) ;
//--------------------------------------------------- 
always_ff@(posedge clk_i,negedge rstn_i)begin
    if(!rstn_i)begin 
        tx_wbuf <= '0;    
    end     
    else if(mem_master.rvalid) begin
        tx_wbuf <= mem_master.rdata;
    end
    else if(dma_tx_valid&dma_tx_ready) begin
        tx_wbuf <= {8'b0,tx_wbuf[31:8]};
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        tx_bcnt <= 'b0;
    end
    else if((dma_reset|ifg_end)||(dma_cs == DMA_WIT))begin 
        tx_bcnt <= 'b0;
    end
    else if((dma_cs == DMA_GAP)||(dma_tx_ready&dma_tx_valid)) begin
        tx_bcnt <= tx_bcnt + 1'b1;
    end
end
//--------------------------------------------------------
always_ff @ (posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_reset <= '0;
    end
    else if(dma_tx_en&dma_tx_need&(dma_cs == DMA_IDLE)&(mem_cs == MEM_IDLE))begin // auto reset in BD mode 
        dma_reset <= '1;
    end
    else if(dma_reset) begin 
        dma_reset <= '0;
    end
end
//--------------------------------------------------------
assign tx_eof = bdu_eof&dma_tx_valid&((tx_bcnt + 1'b1) == bdu_sz);
assign tx_sof = bdu_sof&dma_tx_valid&dma_tx_ready&(tx_bcnt == 0);
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_tx_busy <= '0;
    end
    else if((bdu_cs == MST_IDLE)&&(mem_cs == MEM_IDLE)&&(dma_cs == DMA_IDLE)) begin 
        dma_tx_busy <= '0;
    end
    else begin 
        dma_tx_busy <= '1;
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        dma_tx_err <= '0;
    end
    else if(dma_reset) begin 
        dma_tx_err <= '0;
    end
    else begin 
        if(bdu_sz == 'b0) dma_tx_err[0] <= 1'b1; //data size should be more than 1 words 
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_sz  <= 16'h256;
        bdu_eof <= 1'b0;
        bdu_sof <= 1'b0;
    end
    else if ((bdu_rd_cnt_q == 1)&bdu_master.rvalid) begin 
        bdu_sz  <= bdu_master.rdata[15:0];
        bdu_eof <= bdu_master.rdata[30];
        bdu_sof <= bdu_master.rdata[31];
    end
end
//--------------------------------------------------------
always_ff @(posedge clk_i,negedge rstn_i) begin 
    if(rstn_i == 0)begin 
        bdu_next <= 1'b0;
    end
    else if (dma_reset) begin 
        bdu_next <= 1'b0;
    end
    else begin
        if(tx_eof&dma_tx_ready) bdu_next <= 1'b1;
    end
end
//--------------------------------------------------------
assign ifg_set = mac_tx_ifg + 1;
assign ifg_end = ((dma_cs == DMA_GAP)&&(tx_bcnt == {ifg_set,6'b0}));
//--------------------------------------------------------

endmodule
